package com.reebake.ideal.security.oauth2.client;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ObjectUtil;
import com.reebake.ideal.security.core.LoginSuccessEvent;
import com.reebake.ideal.security.entity.AccessTokenEntity;
import com.reebake.ideal.security.entity.UserDetailsEntity;
import com.reebake.ideal.security.oauth2.entity.OAuth2AuthenticationResult;
import com.reebake.ideal.security.oauth2.properties.OAuth2ClientSecurityProperties;
import com.reebake.ideal.security.service.UserSecurityService;
import com.reebake.ideal.security.util.JwtAuthUtil;
import com.reebake.ideal.servlet.core.ResponseBodyWrapper;
import com.reebake.ideal.servlet.util.WebUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.util.Collection;

@RequiredArgsConstructor
public class SmartOAuth2AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    private final ResponseBodyWrapper responseBodyWrapper;
    private final UserSecurityService userSecurityService;
    private final OAuth2BindUserRepository oAuth2BindUserRepository;
    private final OAuth2ClientSecurityProperties oAuth2ClientSecurityProperties;
    private final ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {
        OAuth2AuthenticationToken oauth2AuthenticationToken = (OAuth2AuthenticationToken) authentication;
        String registrationId = oauth2AuthenticationToken.getAuthorizedClientRegistrationId();
        OAuth2User oAuth2User = ((OAuth2AuthenticationToken) authentication).getPrincipal();
        String username = oAuth2User.getName();
        String saveKey = oAuth2BindUserRepository.save(registrationId, username, oAuth2User.getAttributes());

        String redirectUrl;
        UserDetailsEntity userDetailsEntity = userSecurityService.loadUserByThirdParty(username, registrationId);
        LoginSuccessEvent loginSuccessEvent = new LoginSuccessEvent(this, username
                , userDetailsEntity == null ? null : userDetailsEntity.getUserId(), registrationId);
        applicationEventPublisher.publishEvent(loginSuccessEvent);

        Boolean isBound = ObjectUtil.isNotNull(userDetailsEntity);
        if (!isBound) {
            redirectUrl = oAuth2ClientSecurityProperties.getBindUrl();
        } else {
            redirectUrl = oAuth2ClientSecurityProperties.getLoginFormUrl();
        }
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(redirectUrl);
        String securityIdParamName = LambdaUtil.getFieldName(OAuth2AuthenticationResult::getSecurityId);
        String securityId = Base64.encode(saveKey);
        uriComponentsBuilder.queryParam(securityIdParamName, securityId);
        redirectUrl = uriComponentsBuilder.build().toUriString();
        if (WebUtil.isAjaxRequest(request)) {
            OAuth2AuthenticationResult oAuth2AuthenticationResult = new OAuth2AuthenticationResult();
            oAuth2AuthenticationResult.setRedirectUrl(redirectUrl);
            oAuth2AuthenticationResult.setSecurityId(securityId);
            oAuth2AuthenticationResult.setIsBound(isBound);
            if (isBound) {
                String userId = userDetailsEntity.getUserId();
                Collection<String> authorities = userDetailsEntity.getAuthorities();
                AccessTokenEntity accessTokenEntity = JwtAuthUtil.generate(username, userId, authorities, oAuth2ClientSecurityProperties.getAccessTokenExpireIn());
                oAuth2AuthenticationResult.setAccessToken(accessTokenEntity.getAccessToken());
            }
            responseBodyWrapper.convertAndWrite(response, oAuth2AuthenticationResult);
        } else {
            getRedirectStrategy().sendRedirect(request, response, redirectUrl);
        }
    }
}
